package com.example.backend.service;

import com.example.backend.annotation.LogExecution;
import com.example.backend.domain.entity.Notification;
import com.example.backend.domain.enums.NotificationPriority;
import com.example.backend.domain.enums.NotificationStatus;
import com.example.backend.dto.*;
import com.example.backend.exception.BusinessException;
import com.example.backend.repository.NotificationRepository;
import com.example.backend.repository.spec.NotificationSpecifications;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Instant;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 通知服务
 */
@Service
@Transactional
public class NotificationService {
    
    private final NotificationRepository notificationRepository;
    private final ObjectMapper objectMapper;
    
    public NotificationService(NotificationRepository notificationRepository, ObjectMapper objectMapper) {
        this.notificationRepository = notificationRepository;
        this.objectMapper = objectMapper;
    }
    
    @LogExecution("创建通知")
    @CacheEvict(value = "notifications", allEntries = true)
    public Notification createNotification(SendNotificationRequest request) {
        try {
            Notification notification = Notification.builder()
                    .userId(request.getUserId())
                    .title(request.getTitle())
                    .content(request.getContent())
                    .type(request.getType())
                    .channel(request.getChannel())
                    .priority(request.getPriority())
                    .actionUrl(request.getActionUrl())
                    .metadata(request.getMetadata() != null ? objectMapper.writeValueAsString(request.getMetadata()) : null)
                    .scheduledAt(request.getScheduledAt())
                    .status(NotificationStatus.UNREAD)
                    .build();
            
            return notificationRepository.save(notification);
        } catch (JsonProcessingException e) {
            throw new BusinessException("通知元数据序列化失败: " + e.getMessage());
        }
    }
    
    @LogExecution("获取通知")
    @Cacheable(value = "notifications", key = "#id")
    @Transactional(readOnly = true)
    public Optional<Notification> findById(Long id) {
        return notificationRepository.findById(id);
    }
    
    @LogExecution("获取用户通知列表")
    @Transactional(readOnly = true)
    public Page<NotificationDto> findByUserId(Long userId, Pageable pageable) {
        Page<Notification> notifications = notificationRepository.findByUserId(userId, pageable);
        return notifications.map(this::convertToDto);
    }
    
    @LogExecution("按条件查询通知")
    @Transactional(readOnly = true)
    public Page<NotificationDto> findByFilter(NotificationFilter filter, Pageable pageable) {
        Specification<Notification> spec = NotificationSpecifications.withFilter(filter);
        Page<Notification> notifications = notificationRepository.findAll(spec, pageable);
        return notifications.map(this::convertToDto);
    }
    
    @LogExecution("更新通知")
    @CacheEvict(value = "notifications", allEntries = true)
    public Notification updateNotification(Long id, UpdateNotificationRequest request) {
        Notification notification = notificationRepository.findById(id)
                .orElseThrow(() -> new BusinessException("通知不存在"));
        
        if (request.getTitle() != null) {
            notification.setTitle(request.getTitle());
        }
        if (request.getContent() != null) {
            notification.setContent(request.getContent());
        }
        if (request.getType() != null) {
            notification.setType(request.getType());
        }
        if (request.getChannel() != null) {
            notification.setChannel(request.getChannel());
        }
        if (request.getStatus() != null) {
            notification.setStatus(request.getStatus());
            if (request.getStatus() == NotificationStatus.READ && notification.getReadAt() == null) {
                notification.setReadAt(Instant.now());
            }
        }
        if (request.getPriority() != null) {
            notification.setPriority(request.getPriority());
        }
        if (request.getActionUrl() != null) {
            notification.setActionUrl(request.getActionUrl());
        }
        if (request.getMetadata() != null) {
            try {
                notification.setMetadata(objectMapper.writeValueAsString(request.getMetadata()));
            } catch (JsonProcessingException e) {
                throw new BusinessException("通知元数据序列化失败: " + e.getMessage());
            }
        }
        if (request.getScheduledAt() != null) {
            notification.setScheduledAt(request.getScheduledAt());
        }
        
        return notificationRepository.save(notification);
    }
    
    @LogExecution("标记通知为已读")
    @CacheEvict(value = "notifications", allEntries = true)
    public Notification markAsRead(Long id) {
        Notification notification = notificationRepository.findById(id)
                .orElseThrow(() -> new BusinessException("通知不存在"));
        
        if (notification.getStatus() == NotificationStatus.UNREAD) {
            notification.setStatus(NotificationStatus.READ);
            notification.setReadAt(Instant.now());
            return notificationRepository.save(notification);
        }
        
        return notification;
    }
    
    @LogExecution("批量标记为已读")
    @CacheEvict(value = "notifications", allEntries = true)
    public int markAsReadBatch(List<Long> ids) {
        return notificationRepository.updateStatusByIds(ids, NotificationStatus.READ, Instant.now());
    }
    
    @LogExecution("批量标记为未读")
    @CacheEvict(value = "notifications", allEntries = true)
    public int markAsUnreadBatch(List<Long> ids) {
        return notificationRepository.updateStatusByIds(ids, NotificationStatus.UNREAD, null);
    }
    
    @LogExecution("删除通知")
    @CacheEvict(value = "notifications", allEntries = true)
    public void deleteNotification(Long id) {
        if (!notificationRepository.existsById(id)) {
            throw new BusinessException("通知不存在");
        }
        notificationRepository.deleteById(id);
    }
    
    @LogExecution("批量删除通知")
    @CacheEvict(value = "notifications", allEntries = true)
    public int deleteBatch(List<Long> ids) {
        return notificationRepository.deleteByIds(ids);
    }
    
    @LogExecution("获取未读数量")
    @Transactional(readOnly = true)
    public UnreadCountDto getUnreadCount(Long userId) {
        long unreadCount = notificationRepository.countByUserIdAndStatus(userId, NotificationStatus.UNREAD);
        long urgentCount = notificationRepository.countByUserIdAndPriority(userId, NotificationPriority.URGENT);
        long highPriorityCount = notificationRepository.countByUserIdAndPriority(userId, NotificationPriority.HIGH);
        
        return UnreadCountDto.builder()
                .userId(userId)
                .unreadCount(unreadCount)
                .urgentCount(urgentCount)
                .highPriorityCount(highPriorityCount)
                .build();
    }
    
    @LogExecution("获取通知统计")
    @Transactional(readOnly = true)
    public NotificationStatsDto getNotificationStats(Long userId) {
        long totalCount = notificationRepository.countByUserIdAndStatus(userId, null);
        long unreadCount = notificationRepository.countByUserIdAndStatus(userId, NotificationStatus.UNREAD);
        long readCount = notificationRepository.countByUserIdAndStatus(userId, NotificationStatus.READ);
        long archivedCount = notificationRepository.countByUserIdAndStatus(userId, NotificationStatus.ARCHIVED);
        long deletedCount = notificationRepository.countByUserIdAndStatus(userId, NotificationStatus.DELETED);
        
        Instant today = Instant.now().minusSeconds(24 * 60 * 60);
        Instant week = Instant.now().minusSeconds(7 * 24 * 60 * 60);
        Instant month = Instant.now().minusSeconds(30 * 24 * 60 * 60);
        
        long todayCount = notificationRepository.countByUserIdAndCreatedAtBetween(userId, today, Instant.now());
        long weekCount = notificationRepository.countByUserIdAndCreatedAtBetween(userId, week, Instant.now());
        long monthCount = notificationRepository.countByUserIdAndCreatedAtBetween(userId, month, Instant.now());
        
        return NotificationStatsDto.builder()
                .totalCount(totalCount)
                .unreadCount(unreadCount)
                .readCount(readCount)
                .archivedCount(archivedCount)
                .deletedCount(deletedCount)
                .todayCount(todayCount)
                .weekCount(weekCount)
                .monthCount(monthCount)
                .build();
    }
    
    @LogExecution("搜索通知")
    @Transactional(readOnly = true)
    public Page<NotificationDto> searchNotifications(Long userId, String keyword, Pageable pageable) {
        Page<Notification> notifications = notificationRepository.searchByUserIdAndKeyword(userId, keyword, pageable);
        return notifications.map(this::convertToDto);
    }
    
    @LogExecution("获取未读通知")
    @Transactional(readOnly = true)
    public List<NotificationDto> getUnreadNotifications(Long userId) {
        List<Notification> notifications = notificationRepository.findUnreadByUserId(userId);
        return notifications.stream().map(this::convertToDto).collect(Collectors.toList());
    }
    
    @LogExecution("获取紧急通知")
    @Transactional(readOnly = true)
    public List<NotificationDto> getUrgentNotifications(Long userId) {
        List<Notification> notifications = notificationRepository.findUrgentByUserId(userId);
        return notifications.stream().map(this::convertToDto).collect(Collectors.toList());
    }
    
    private NotificationDto convertToDto(Notification notification) {
        return NotificationDto.builder()
                .id(notification.getId())
                .userId(notification.getUserId())
                .title(notification.getTitle())
                .content(notification.getContent())
                .type(notification.getType())
                .channel(notification.getChannel())
                .status(notification.getStatus())
                .priority(notification.getPriority())
                .actionUrl(notification.getActionUrl())
                .metadata(notification.getMetadata())
                .readAt(notification.getReadAt())
                .sentAt(notification.getSentAt())
                .scheduledAt(notification.getScheduledAt())
                .createdAt(notification.getCreatedAt())
                .updatedAt(notification.getUpdatedAt())
                .build();
    }
}
